package javango.core;

import java.lang.reflect.Method;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javango.api.MethodObjectParams;
import javango.api.Url;
import javango.http.HttpException;
import javango.http.HttpRequest;

import org.apache.commons.beanutils.locale.LocaleConvertUtilsBean;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;

public class RegUrl implements Url {
	private final static Log log = LogFactory.getLog(RegUrl.class);
	
	public String pattern;
	public Class<?> clazz;
	public String methodName;
	
	// cached copies of the pattern
	protected Pattern p;
	
	// cached copies of the method and parameter types
	public Method method;
	public Class<?>[] paramTypes;
		
	@Inject
	public RegUrl(@Assisted("pattern") String pattern, @Assisted Class<?> clazz, @Assisted("methodName") String methodName) {
		super();
		this.clazz = clazz;
		this.methodName = methodName;
		this.setPattern(pattern);
	}
		
	@Override
	public String toString() {
		return "Url: '" + this.pattern + "'";
	}

	protected void getMethodInformation(int params) {
		Class<?> currentClass = this.clazz;
		while(currentClass != null) {
			Method[] methods = currentClass.getDeclaredMethods();		
			for(Method m: methods) {
				if (this.methodName.equals(m.getName()) && params+1 == m.getParameterTypes().length) {
					this.paramTypes = m.getParameterTypes();
					this.method = m;
					return;
				}
			}
			currentClass = currentClass.getSuperclass();
		}
	}
	
	public boolean matches(String url) {
		return p.matcher(url).matches();
	}
	
	public MethodObjectParams getMethodObjectParams(String url, HttpRequest req) throws HttpException {
		Matcher m = p.matcher(url);
		if(m.matches()){
			synchronized (RegUrl.class) {
				if (paramTypes == null) {
					getMethodInformation(m.groupCount());
					if (this.method == null) {
						// TODO if in debug mode show a more detailed message
						// Unable to find Views.class[methodName(HttpResponse, otherparams)]
						// Views contained the following methods
						// ... (is it possible to show methods from a class?)
						log.error("Unable to find method matching name='" + this.methodName + "' and param count = " + m.groupCount() );
						throw new HttpException("Unable to find method matching name='" + this.methodName + "' and param count = " + m.groupCount() );
					}

				}
			}
			Object[] params = new Object[m.groupCount()+1];
			params[0] = req;
			for (int i=1;i<=m.groupCount();i++) {
				LocaleConvertUtilsBean converter = new LocaleConvertUtilsBean();
				params[i] = converter.convert(m.group(i), paramTypes[i]);
			}

			return new MethodObjectParams(this.clazz, method, params);
		} else {
			log.warn("Called execute on url,  but pattern did not match: " + url);
			return null;
		}
	}

	protected String getPattern() {
		return pattern;
	}

	protected void setPattern(String pattern) {
		p = Pattern.compile(pattern);
		this.pattern = pattern;
	}	
}
